/**
 * This code is released under lgpl v3 license.
 * All rights reserved.
*/

package flash.events;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.Window;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import flash.events.AS3EventHandler;


/**
 * base class for EventDispatcher and subclass, for add event handler.
 * T, subclass of EventDispatcher, a jso object from fabridge
 * @author yuan yunchang    dukerr@gmail.com
 */
public abstract class BaseWrapper<T extends EventDispatcher> implements HasAS3EventHandler{
    /**
     * the bridge name used for js-as3 communication
     */
    protected String bridgeName;
    /**
     * a native javascript object from fabridge
     */
    protected T nativeAS3Obj ;
    /**
     * a javascript proxy function for event process
     */
    protected JavaScriptObject nativeEventProxy;
    /**
     * our event handler collection
     */
    private  Map<String,List<AS3EventHandler>> handlers = new HashMap<String,List<AS3EventHandler>>();

    /**
     * create a new native javascript object releate to this wrapper.
     * subclass should override this method to create their own jso object
     * and application should createNativeAS3Obj or setNativeAS3Obj before other call
     * @param bridgeName
     */
    abstract public  void createNativeAS3Obj();

    /**
     * 
     * @return
     */
    public T getNativeAS3Obj() {
        return nativeAS3Obj;
    }

    public void setNativeAS3Obj(T nativeAS3Obj) {
        this.nativeAS3Obj = nativeAS3Obj;
    }
    
    /**
     * init event process proxy,if nativeEventProxy==null,create it
     *
     */
    protected void initEventProxy(){
        if(nativeEventProxy==null){
            BaseWrapper instance = this;
            initEventProxyImpl(instance);
        }
    }
    /**
     * init event proxy impl. it should not be used outside of this class
     * @param instance
     */
    private native void initEventProxyImpl(BaseWrapper instance)/*-{
        this.@flash.events.BaseWrapper::nativeEventProxy=function(e){
             instance.@flash.events.BaseWrapper::dispatchEvent(Lflash/events/Event;)(e)
        }
    }-*/;
    /**
     * add event listener to a fabridge object.
     * @param eventType     event type
     * @param nativeObj     subclass of EventDispatcher that can add event listener and remove event listener
     * @param func
     */
    private  void addNativeListener(String eventType,EventDispatcher nativeObj,JavaScriptObject func){
        nativeObj.addEventListener(eventType, func);
    };
    private  void removeNativeListener(String eventType,EventDispatcher nativeObj,JavaScriptObject func){
        nativeObj.removeEventListener(eventType, func);
    };

    /**
     *
     * @param bridgeName    the bridge name
     */
    public BaseWrapper(String bridgeName) {
        this(bridgeName,false);
    }
    /**
     *
     * @param bridgeName    the bridge name
     * @param createNative  if create native object
     */
    public BaseWrapper(String bridgeName,boolean createNative) {
        this.bridgeName = bridgeName;
        initEventProxy();
        if(createNative){
            createNativeAS3Obj();
        }
    }


    /**
     * add a event handler
     * @param handler           a AS3EventHandler
     * @param eventType         event type, same as as3 event definition
     */
    public void addHandler(AS3EventHandler handler,String eventType){
        //Window.alert(""+stage.getFrameRate());
        List<AS3EventHandler> hlds = handlers.get(eventType);
        if(hlds==null){
            List<AS3EventHandler> newlist = new ArrayList<AS3EventHandler>();
            hlds = newlist;
            handlers.put(eventType, hlds);
            addNativeListener(eventType,nativeAS3Obj,nativeEventProxy);
        }
        hlds.add(handler);

    }
    /**
     * remove event handler
     * @param handler
     * @param eventType
     */
    public   void removeHandler(AS3EventHandler handler,String eventType){
        List<AS3EventHandler> hlds = handlers.get(eventType);
        if(hlds==null){
            removeNativeListener(eventType,nativeAS3Obj,nativeEventProxy);
            return;
        }
        hlds.remove(handler);
        if(hlds.size()==0){
            handlers.remove(eventType);
            removeNativeListener(eventType,nativeAS3Obj,nativeEventProxy);
        }
    }
    /**
     * event dispatcher. called by event proxy
     * @param event
     */
    private void dispatchEvent(Event event){
        //Window.alert("in dispatchEvent:"+event.getType());
        String type = event.getType();
        List<AS3EventHandler> hlds = handlers.get(type);
        if(hlds==null){
            throw new IllegalStateException("can not found handler for event type:"+type);
        }
        for(AS3EventHandler hd : hlds){
            hd.onEvent(event);
        }
    }

    public String getBridgeName() {
        return bridgeName;
    }

    public void setBridgeName(String bridgeName) {
        this.bridgeName = bridgeName;
    }
    
}
